/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.skatiger.jrove.impl.jaxb;

import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.skatiger.jrove.TagHandler;
import org.skatiger.jrove.TagLibrary;
import org.skatiger.jrove.impl.DefaultConfigurator;
import org.skatiger.jrove.impl.jaxb.taglib.Function;
import org.skatiger.jrove.impl.jaxb.taglib.Tag;
import org.skatiger.jrove.impl.jaxb.taglib.Taglib;
import org.skatiger.jrove.util.StringUtils;

class JaxbTaglibConfig {
	private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory
			.getLogger(JaxbTaglibConfig.class);
	private DefaultConfigurator configurator;
	private Unmarshaller unmarshaller;

	public JaxbTaglibConfig(DefaultConfigurator configurator) {
		this.configurator = configurator;
	}

	private Unmarshaller getUnmarshaller() throws JAXBException {
		if (unmarshaller == null) {
			JAXBContext context = JAXBContext
					.newInstance(JaxbConfigReader.TAGLIB_CONTEXT_PATH);
			unmarshaller = context.createUnmarshaller();
		}
		return unmarshaller;
	}

	TagLibrary create(URL url) throws Exception {
		Taglib taglib = (Taglib) getUnmarshaller().unmarshal(url);
		String libraryClass = taglib.getLibraryClass();
		if (StringUtils.isNotEmpty(libraryClass)) {
			Class<TagLibrary> tagLibraryClass = JaxbConfigReader.createClass(
					TagLibrary.class, libraryClass);
			return (TagLibrary) tagLibraryClass.newInstance();
		} else {
			TagLibraryImpl library = null;
			String namespace = taglib.getNamespace();
			if (StringUtils.isBlank(namespace)) {
				throw new NullPointerException(
						"taglib's namespace can not be empty.");
			}
			library = create(taglib.getNamespace());
			processTaglib(library, taglib);
			return library;
		}

	}

	private void processTaglib(TagLibraryImpl library, Taglib taglib)
			throws Exception {
		List<Tag> tags = taglib.getTag();
		for (Tag tag : tags) {
			processTag(library, tag);
		}
		List<Function> functions = taglib.getFunction();
		for (Function function : functions) {
			processFunction(library, function);
		}
	}

	private void processFunction(TagLibraryImpl library, Function function)
			throws Exception {
		String functionName = function.getFunctionName();
		String functionClass = function.getFunctionClass();
		String functionSignature = function.getFunctionSignature();
		Class<Object> _functionClass = JaxbConfigReader.createClass(
				Object.class, functionClass);
		Method m = JaxbConfigReader.createMethod(_functionClass,
				functionSignature);
		library.putFunction(functionName, m);
	}

	private void processTag(TagLibraryImpl library, Tag tag) throws Exception {
		if (logger.isDebugEnabled()) {
			logger.debug("process tag " + tag);
		}
		String tagName = tag.getTagName();
		String source = tag.getSource();

		if (StringUtils.isNotBlank(source)) {
			if (logger.isDebugEnabled()) {
				logger.debug("tag is source tag");
			}
			URL url = configurator.getResourceResolver().resolveUrl(source);
			library.putUserTag(tagName, url);
			return;
		}

		String handlerClass = tag.getHandlerClass();
		String componentType = tag.getComponentClass();
		Class<TagHandler> _handlerClass = null;

		if (StringUtils.isNotBlank(handlerClass)) {
			_handlerClass = JaxbConfigReader.createClass(TagHandler.class,
					handlerClass);
		}

		if (StringUtils.isBlank(componentType)) {
			if (_handlerClass == null) {
				throw new ClassNotFoundException("handler class "
						+ _handlerClass + " not found.");
			}
			if (logger.isDebugEnabled()) {
				logger.debug("found handlerClass tag " + _handlerClass);
			}
			library.putTagHandler(tagName, _handlerClass);
			return;
		}
		// if renderer class not empty
		String rendererType = tag.getRendererType();
		if (_handlerClass != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("put component: " + componentType
						+ ", rendererType: " + rendererType
						+ ", handlerClass: " + _handlerClass);
			}
			library.putComponent(tagName, componentType, rendererType,
					_handlerClass);
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("put component: " + componentType
						+ ", rendererType: " + rendererType);
			}
			library.putComponent(tagName, componentType, rendererType);
		}
	}

	private static TagLibraryImpl create(String namespace) {
		return new TagLibraryImpl(namespace);
	}
}
